package orm

import (
	"fmt"
	"github.com/go-redis/redis/v8"
	"github.com/liyuan1125/gorm-cache"
	redis2 "github.com/liyuan1125/gorm-cache/store/redis"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/schema"
	"os"
	"time"
	"zsc-boot/core"
)

const cbConfKey = "mysql"

type Db struct {
	User         string `mapstructure:"user"`
	Password     string `mapstructure:"password"`
	Host         string `mapstructure:"host"`
	Port         uint   `mapstructure:"port"`
	Database     string `mapstructure:"dbname"`
	MaxIdleConns int    `mapstructure:"max_idle_conns"`
	MaxOpenConns int    `mapstructure:"max_open_conns"`
	UseFileLog   bool   `mapstructure:"use-file_log"`
}

var dbMap = make(map[string]*gorm.DB)

func (d *Db) load() (db *gorm.DB, err error) {
	dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local",
		d.User, d.Password, d.Host, d.Port, d.Database)

	if d.UseFileLog {
		db, err = gorm.Open(mysql.New(mysql.Config{
			DSN:               dsn,
			DefaultStringSize: 255,
		}), &gorm.Config{
			Logger:                                   NewOrmLogByZap(),
			DisableForeignKeyConstraintWhenMigrating: true,
		})
	} else {
		db, err = gorm.Open(mysql.New(mysql.Config{
			DSN:               dsn,
			DefaultStringSize: 255,
		}), &gorm.Config{
			DisableForeignKeyConstraintWhenMigrating: true,
		})
	}

	if core.GetViper().GetString("redis.addr") != "" {
		redisClient := redis.NewClient(&redis.Options{Addr: core.GetViper().GetString("redis.addr")})

		cacheConfig := &cache.Config{
			Store:      redis2.NewWithDb(redisClient), // OR redis2.New(&redis.Options{Addr:"6379"})
			Serializer: &cache.DefaultJSONSerializer{},
		}

		cachePlugin := cache.New(cacheConfig)

		if err = db.Use(cachePlugin); err != nil {
			fmt.Println(err.Error())
			os.Exit(1)
		}
	}

	if err != nil {
		panic(fmt.Errorf("数据库连接失败%s", err))
	}
	sqlDB, err := db.DB()
	if err != nil {
		return nil, fmt.Errorf("获取数据库通用接口失败%s", err)
	}
	// SetMaxIdleConns 用于设置连接池中空闲连接的最大数量。
	sqlDB.SetMaxIdleConns(d.MaxIdleConns)
	// SetMaxOpenConns 设置打开数据库连接的最大数量。
	sqlDB.SetMaxOpenConns(d.MaxOpenConns)
	// SetConnMaxLifetime 设置了连接可复用的最大时间。
	sqlDB.SetConnMaxLifetime(time.Hour)

	return db, nil
}

func GetDb(name ...string) *gorm.DB {
	key := cbConfKey
	//确定key
	if len(name) > 0 {
		key = name[0]
	}

	if db, ok := dbMap[key]; ok {
		return db.Session(&gorm.Session{NewDB: true})
	}

	var (
		conf Db
		err  error
		db   *gorm.DB
	)
	//获取配置
	if conf, err = core.Conf[Db](key); err != nil {
		panic(err)
	}

	//加载
	if db, err = conf.load(); err != nil {
		panic(err)
	}

	//填入map
	dbMap[key] = db

	return db.Session(&gorm.Session{NewDB: true})
}

func AutoMigrate(dst ...TableComment) {
	if core.GetViper().GetBool("mysql.init-db") == false {
		return
	}

	anyList := make([]interface{}, 0, len(dst))
	for _, table := range dst {
		anyList = append(anyList, table)
	}
	if err := GetDb().AutoMigrate(anyList...); err != nil {
		panic(err)
	}

	for _, v := range dst {
		sql := fmt.Sprintf("ALTER TABLE %s COMMENT '%s';", "`"+v.TableName()+"`", v.TableComment())
		if err := GetDb().Exec(sql).
			Error; err != nil {
			panic(err)
		}
	}
}

type TableComment interface {
	schema.Tabler
	TableComment() string
}
